﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Globalization;
using System.Linq;
using System.Net;
using System.ServiceModel.Syndication;
using System.Threading;
using System.Windows;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;
using Microsoft.Phone.Net.NetworkInformation;
using System.Windows.Threading;

namespace GooNews.ViewModels
{
  /// <summary>
  /// 
  /// </summary>
  public class Feed : FeedInfo
  {
    /// <summary>
    /// Initializes a new instance of the <see cref="Feed"/> class.
    /// </summary>
    public Feed()
    {
      WatchCollection();
    }

    ObservableCollection<FeedItem> _feedItems = new ObservableCollection<FeedItem>();

    /// <summary>
    /// Gets or sets the feed items.
    /// </summary>
    /// <value>
    /// The feed items.
    /// </value>
    public ObservableCollection<FeedItem> FeedItems
    {
      get { return _feedItems; }
      set
      {
        if (_feedItems != null && _isFeedEmptyHandler != null) _feedItems.CollectionChanged -= _isFeedEmptyHandler;
        _feedItems = value;
        RaisePropertyChanged("FeedItems");
        RaisePropertyChanged("IsFeedEmpty");
        WatchCollection();
        _isLoaded = true;
      }
    }

    NotifyCollectionChangedEventHandler _isFeedEmptyHandler = null;

    /// <summary>
    /// Watches the collection.
    /// </summary>
    private void WatchCollection()
    {
      _isFeedEmptyHandler = new NotifyCollectionChangedEventHandler(FeedItems_CollectionChanged);
      FeedItems.CollectionChanged += _isFeedEmptyHandler;
    }

    /// <summary>
    /// Handles the CollectionChanged event of the FeedItems control.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="System.Collections.Specialized.NotifyCollectionChangedEventArgs"/> instance containing the event data.</param>
    void FeedItems_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
      RaisePropertyChanged("IsFeedEmpty");
    }

    /// <summary>
    /// Gets a value indicating whether this instance is feed empty.
    /// </summary>
    /// <value>
    /// 	<c>true</c> if this instance is feed empty; otherwise, <c>false</c>.
    /// </value>
    [XmlIgnore]
    public bool IsFeedEmpty
    {
      get { return FeedItems.Count == 0 && string.IsNullOrEmpty(ErrorMessage) && !IsBusy; }
    }

    /// <summary>
    /// Loads the feed async.
    /// </summary>
    public void LoadFeedAsync()
    {
      if (!NetworkInterface.GetIsNetworkAvailable())
      {
        ErrorMessage = "No Network is Available. Try again later.";
      }
      else
      {
        FeedItems.Clear();
        ThreadPool.QueueUserWorkItem(new WaitCallback(DoLoadFeed));
      }
    }

    /// <summary>
    /// Does the load feed.
    /// </summary>
    /// <param name="ignore">The ignore.</param>
    void DoLoadFeed(object ignore)
    {
      IsBusy = true;

      var req = HttpWebRequest.CreateHttp(Url);
      req.BeginGetResponse(r =>
        {
          try
          {
            var response = req.EndGetResponse(r);
            var strm = response.GetResponseStream();
            var doc = XDocument.Load(strm);

            // Google RSS Feeds aren't used standard date format
            FixDates(doc.Descendants("pubDate"));
            FixDates(doc.Descendants("lastBuildDate"));

            XmlReader rdr = doc.CreateReader();
            var rssReader = new Rss20FeedFormatter();
            if (rssReader.CanRead(rdr))
            {
              rssReader.ReadFrom(rdr);
              List<FeedItem> newItems = new List<FeedItem>();
              foreach (var i in rssReader.Feed.Items)
              {
                newItems.Add(new FeedItem(i));
              }
              SmartDispatcher.BeginInvoke(() =>
                {
                  FeedItems.Clear();
                  newItems.OrderByDescending(i => i.DatePublished).ToList().ForEach(i => FeedItems.Add(i));
                  RaiseLoadComplete();
                });
              _isLoaded = true;
            }
          }
          catch
          {
            SmartDispatcher.BeginInvoke(() =>
              {
                FeedItems.Clear();
                ErrorMessage = "Failed to load News Items";
              });
          }
          SmartDispatcher.BeginInvoke(() => IsBusy = false);
        }, null);
    }

    /// <summary>
    /// Fixes the wierd Google dates.
    /// </summary>
    /// <param name="eles">The eles.</param>
    void FixDates(IEnumerable<XElement> eles)
    {
      //"Mon, 05 Oct 2009 08:00:06 GMT";
      //const string dateFormat = "ddd, dd MMM yyyy HH:mm:ss K";

      //"Sat, 18 Dec 2010 22:35:08 GMT+00:00"
      string[] badFormats = new string[] 
        {
          "ddd, dd MMM yyyy HH:mm:ss ZK", // Google News
          "yyyy-MM-ddTHH:mm:ssK"          // Bing News
        };

      foreach (var ele in eles)
      {
        foreach (var fmt in badFormats)
        {
          DateTime date;
          if (DateTime.TryParseExact(ele.Value, fmt, CultureInfo.InvariantCulture, DateTimeStyles.None, out date))
          {
            ele.Value = date.ToUniversalTime().ToString("R", CultureInfo.InvariantCulture);
          }
        }
      }

    }

    bool _isBusy = false;

    /// <summary>
    /// Gets or sets a value indicating whether this instance is busy.
    /// </summary>
    /// <value>
    ///   <c>true</c> if this instance is busy; otherwise, <c>false</c>.
    /// </value>
    [XmlIgnore]
    public bool IsBusy
    {
      get { return _isBusy; }
      set
      {
        _isBusy = value;
        RaisePropertyChanged("IsBusy");

      }
    }

    /// <summary>
    /// Occurs when [load complete].
    /// </summary>
    public event EventHandler LoadComplete;

    /// <summary>
    /// Raises the load complete.
    /// </summary>
    void RaiseLoadComplete()
    {
      if (LoadComplete != null)
      {
        Deployment.Current.Dispatcher.BeginInvoke(() => LoadComplete(this, new EventArgs()));
      }
    }

    string _errorMessage;

    /// <summary>
    /// Gets or sets the error message.
    /// </summary>
    /// <value>
    /// The error message.
    /// </value>
    [XmlIgnore]
    public string ErrorMessage
    {
      get { return _errorMessage; }
      set
      {
        _errorMessage = value;
        RaisePropertyChanged("ErrorMessage");
      }
    }

    bool _isLoaded = false;

    /// <summary>
    /// Gets or sets a value indicating whether this instance is loaded.
    /// </summary>
    /// <value>
    ///   <c>true</c> if this instance is loaded; otherwise, <c>false</c>.
    /// </value>
    public bool IsLoaded
    {
      get { return _isLoaded; }
      set { _isLoaded = value; }
    }


  }
}
